/*
 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package javax.swing.plaf.basic;

import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;

import java.beans.*;

import java.util.Hashtable;
import java.util.HashMap;

import javax.swing.border.*;
import javax.swing.plaf.*;
import sun.swing.DefaultLookup;
import sun.swing.UIAction;


/**
 * A Basic L&amp;F implementation of ToolBarUI.  This implementation
 * is a "combined" view/controller.
 * <p>
 *
 * @author Georges Saab
 * @author Jeff Shapiro
 */
public class BasicToolBarUI extends ToolBarUI implements SwingConstants {

  protected JToolBar toolBar;
  private boolean floating;
  private int floatingX;
  private int floatingY;
  private JFrame floatingFrame;
  private RootPaneContainer floatingToolBar;
  protected DragWindow dragWindow;
  private Container dockingSource;
  private int dockingSensitivity = 0;
  protected int focusedCompIndex = -1;

  protected Color dockingColor = null;
  protected Color floatingColor = null;
  protected Color dockingBorderColor = null;
  protected Color floatingBorderColor = null;

  protected MouseInputListener dockingListener;
  protected PropertyChangeListener propertyListener;

  protected ContainerListener toolBarContListener;
  protected FocusListener toolBarFocusListener;
  private Handler handler;

  protected String constraintBeforeFloating = BorderLayout.NORTH;

  // Rollover button implementation.
  private static String IS_ROLLOVER = "JToolBar.isRollover";
  private static Border rolloverBorder;
  private static Border nonRolloverBorder;
  private static Border nonRolloverToggleBorder;
  private boolean rolloverBorders = false;

  private HashMap<AbstractButton, Border> borderTable = new HashMap<AbstractButton, Border>();
  private Hashtable<AbstractButton, Boolean> rolloverTable = new Hashtable<AbstractButton, Boolean>();


  /**
   * As of Java 2 platform v1.3 this previously undocumented field is no
   * longer used.
   * Key bindings are now defined by the LookAndFeel, please refer to
   * the key bindings specification for further details.
   *
   * @deprecated As of Java 2 platform v1.3.
   */
  @Deprecated
  protected KeyStroke upKey;
  /**
   * As of Java 2 platform v1.3 this previously undocumented field is no
   * longer used.
   * Key bindings are now defined by the LookAndFeel, please refer to
   * the key bindings specification for further details.
   *
   * @deprecated As of Java 2 platform v1.3.
   */
  @Deprecated
  protected KeyStroke downKey;
  /**
   * As of Java 2 platform v1.3 this previously undocumented field is no
   * longer used.
   * Key bindings are now defined by the LookAndFeel, please refer to
   * the key bindings specification for further details.
   *
   * @deprecated As of Java 2 platform v1.3.
   */
  @Deprecated
  protected KeyStroke leftKey;
  /**
   * As of Java 2 platform v1.3 this previously undocumented field is no
   * longer used.
   * Key bindings are now defined by the LookAndFeel, please refer to
   * the key bindings specification for further details.
   *
   * @deprecated As of Java 2 platform v1.3.
   */
  @Deprecated
  protected KeyStroke rightKey;


  private static String FOCUSED_COMP_INDEX = "JToolBar.focusedCompIndex";

  public static ComponentUI createUI(JComponent c) {
    return new BasicToolBarUI();
  }

  public void installUI(JComponent c) {
    toolBar = (JToolBar) c;

    // Set defaults
    installDefaults();
    installComponents();
    installListeners();
    installKeyboardActions();

    // Initialize instance vars
    dockingSensitivity = 0;
    floating = false;
    floatingX = floatingY = 0;
    floatingToolBar = null;

    setOrientation(toolBar.getOrientation());
    LookAndFeel.installProperty(c, "opaque", Boolean.TRUE);

    if (c.getClientProperty(FOCUSED_COMP_INDEX) != null) {
      focusedCompIndex = ((Integer) (c.getClientProperty(FOCUSED_COMP_INDEX))).intValue();
    }
  }

  public void uninstallUI(JComponent c) {

    // Clear defaults
    uninstallDefaults();
    uninstallComponents();
    uninstallListeners();
    uninstallKeyboardActions();

    // Clear instance vars
    if (isFloating()) {
      setFloating(false, null);
    }

    floatingToolBar = null;
    dragWindow = null;
    dockingSource = null;

    c.putClientProperty(FOCUSED_COMP_INDEX, Integer.valueOf(focusedCompIndex));
  }

  protected void installDefaults() {
    LookAndFeel.installBorder(toolBar, "ToolBar.border");
    LookAndFeel.installColorsAndFont(toolBar,
        "ToolBar.background",
        "ToolBar.foreground",
        "ToolBar.font");
    // Toolbar specific defaults
    if (dockingColor == null || dockingColor instanceof UIResource) {
      dockingColor = UIManager.getColor("ToolBar.dockingBackground");
    }
    if (floatingColor == null || floatingColor instanceof UIResource) {
      floatingColor = UIManager.getColor("ToolBar.floatingBackground");
    }
    if (dockingBorderColor == null ||
        dockingBorderColor instanceof UIResource) {
      dockingBorderColor = UIManager.getColor("ToolBar.dockingForeground");
    }
    if (floatingBorderColor == null ||
        floatingBorderColor instanceof UIResource) {
      floatingBorderColor = UIManager.getColor("ToolBar.floatingForeground");
    }

    // ToolBar rollover button borders
    Object rolloverProp = toolBar.getClientProperty(IS_ROLLOVER);
    if (rolloverProp == null) {
      rolloverProp = UIManager.get("ToolBar.isRollover");
    }
    if (rolloverProp != null) {
      rolloverBorders = ((Boolean) rolloverProp).booleanValue();
    }

    if (rolloverBorder == null) {
      rolloverBorder = createRolloverBorder();
    }
    if (nonRolloverBorder == null) {
      nonRolloverBorder = createNonRolloverBorder();
    }
    if (nonRolloverToggleBorder == null) {
      nonRolloverToggleBorder = createNonRolloverToggleBorder();
    }

    setRolloverBorders(isRolloverBorders());
  }

  protected void uninstallDefaults() {
    LookAndFeel.uninstallBorder(toolBar);
    dockingColor = null;
    floatingColor = null;
    dockingBorderColor = null;
    floatingBorderColor = null;

    installNormalBorders(toolBar);

    rolloverBorder = null;
    nonRolloverBorder = null;
    nonRolloverToggleBorder = null;
  }

  protected void installComponents() {
  }

  protected void uninstallComponents() {
  }

  protected void installListeners() {
    dockingListener = createDockingListener();

    if (dockingListener != null) {
      toolBar.addMouseMotionListener(dockingListener);
      toolBar.addMouseListener(dockingListener);
    }

    propertyListener = createPropertyListener();  // added in setFloating
    if (propertyListener != null) {
      toolBar.addPropertyChangeListener(propertyListener);
    }

    toolBarContListener = createToolBarContListener();
    if (toolBarContListener != null) {
      toolBar.addContainerListener(toolBarContListener);
    }

    toolBarFocusListener = createToolBarFocusListener();

    if (toolBarFocusListener != null) {
      // Put focus listener on all components in toolbar
      Component[] components = toolBar.getComponents();

      for (Component component : components) {
        component.addFocusListener(toolBarFocusListener);
      }
    }
  }

  protected void uninstallListeners() {
    if (dockingListener != null) {
      toolBar.removeMouseMotionListener(dockingListener);
      toolBar.removeMouseListener(dockingListener);

      dockingListener = null;
    }

    if (propertyListener != null) {
      toolBar.removePropertyChangeListener(propertyListener);
      propertyListener = null;  // removed in setFloating
    }

    if (toolBarContListener != null) {
      toolBar.removeContainerListener(toolBarContListener);
      toolBarContListener = null;
    }

    if (toolBarFocusListener != null) {
      // Remove focus listener from all components in toolbar
      Component[] components = toolBar.getComponents();

      for (Component component : components) {
        component.removeFocusListener(toolBarFocusListener);
      }

      toolBarFocusListener = null;
    }
    handler = null;
  }

  protected void installKeyboardActions() {
    InputMap km = getInputMap(JComponent.
        WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);

    SwingUtilities.replaceUIInputMap(toolBar, JComponent.
            WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
        km);

    LazyActionMap.installLazyActionMap(toolBar, BasicToolBarUI.class,
        "ToolBar.actionMap");
  }

  InputMap getInputMap(int condition) {
    if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
      return (InputMap) DefaultLookup.get(toolBar, this,
          "ToolBar.ancestorInputMap");
    }
    return null;
  }

  static void loadActionMap(LazyActionMap map) {
    map.put(new Actions(Actions.NAVIGATE_RIGHT));
    map.put(new Actions(Actions.NAVIGATE_LEFT));
    map.put(new Actions(Actions.NAVIGATE_UP));
    map.put(new Actions(Actions.NAVIGATE_DOWN));
  }

  protected void uninstallKeyboardActions() {
    SwingUtilities.replaceUIActionMap(toolBar, null);
    SwingUtilities.replaceUIInputMap(toolBar, JComponent.
            WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
        null);
  }

  protected void navigateFocusedComp(int direction) {
    int nComp = toolBar.getComponentCount();
    int j;

    switch (direction) {
      case EAST:
      case SOUTH:

        if (focusedCompIndex < 0 || focusedCompIndex >= nComp) {
          break;
        }

        j = focusedCompIndex + 1;

        while (j != focusedCompIndex) {
          if (j >= nComp) {
            j = 0;
          }
          Component comp = toolBar.getComponentAtIndex(j++);

          if (comp != null && comp.isFocusTraversable() && comp.isEnabled()) {
            comp.requestFocus();
            break;
          }
        }

        break;

      case WEST:
      case NORTH:

        if (focusedCompIndex < 0 || focusedCompIndex >= nComp) {
          break;
        }

        j = focusedCompIndex - 1;

        while (j != focusedCompIndex) {
          if (j < 0) {
            j = nComp - 1;
          }
          Component comp = toolBar.getComponentAtIndex(j--);

          if (comp != null && comp.isFocusTraversable() && comp.isEnabled()) {
            comp.requestFocus();
            break;
          }
        }

        break;

      default:
        break;
    }
  }

  /**
   * Creates a rollover border for toolbar components. The
   * rollover border will be installed if rollover borders are
   * enabled.
   * <p>
   * Override this method to provide an alternate rollover border.
   *
   * @since 1.4
   */
  protected Border createRolloverBorder() {
    Object border = UIManager.get("ToolBar.rolloverBorder");
    if (border != null) {
      return (Border) border;
    }
    UIDefaults table = UIManager.getLookAndFeelDefaults();
    return new CompoundBorder(new BasicBorders.RolloverButtonBorder(
        table.getColor("controlShadow"),
        table.getColor("controlDkShadow"),
        table.getColor("controlHighlight"),
        table.getColor("controlLtHighlight")),
        new BasicBorders.RolloverMarginBorder());
  }

  /**
   * Creates the non rollover border for toolbar components. This
   * border will be installed as the border for components added
   * to the toolbar if rollover borders are not enabled.
   * <p>
   * Override this method to provide an alternate rollover border.
   *
   * @since 1.4
   */
  protected Border createNonRolloverBorder() {
    Object border = UIManager.get("ToolBar.nonrolloverBorder");
    if (border != null) {
      return (Border) border;
    }
    UIDefaults table = UIManager.getLookAndFeelDefaults();
    return new CompoundBorder(new BasicBorders.ButtonBorder(
        table.getColor("Button.shadow"),
        table.getColor("Button.darkShadow"),
        table.getColor("Button.light"),
        table.getColor("Button.highlight")),
        new BasicBorders.RolloverMarginBorder());
  }

  /**
   * Creates a non rollover border for Toggle buttons in the toolbar.
   */
  private Border createNonRolloverToggleBorder() {
    UIDefaults table = UIManager.getLookAndFeelDefaults();
    return new CompoundBorder(new BasicBorders.RadioButtonBorder(
        table.getColor("ToggleButton.shadow"),
        table.getColor("ToggleButton.darkShadow"),
        table.getColor("ToggleButton.light"),
        table.getColor("ToggleButton.highlight")),
        new BasicBorders.RolloverMarginBorder());
  }

  /**
   * No longer used, use BasicToolBarUI.createFloatingWindow(JToolBar)
   *
   * @see #createFloatingWindow
   */
  protected JFrame createFloatingFrame(JToolBar toolbar) {
    Window window = SwingUtilities.getWindowAncestor(toolbar);
    JFrame frame = new JFrame(toolbar.getName(),
        (window != null) ? window.getGraphicsConfiguration() : null) {
      // Override createRootPane() to automatically resize
      // the frame when contents change
      protected JRootPane createRootPane() {
        JRootPane rootPane = new JRootPane() {
          private boolean packing = false;

          public void validate() {
            super.validate();
            if (!packing) {
              packing = true;
              pack();
              packing = false;
            }
          }
        };
        rootPane.setOpaque(true);
        return rootPane;
      }
    };
    frame.getRootPane().setName("ToolBar.FloatingFrame");
    frame.setResizable(false);
    WindowListener wl = createFrameListener();
    frame.addWindowListener(wl);
    return frame;
  }

  /**
   * Creates a window which contains the toolbar after it has been
   * dragged out from its container
   *
   * @return a <code>RootPaneContainer</code> object, containing the toolbar.
   * @since 1.4
   */
  protected RootPaneContainer createFloatingWindow(JToolBar toolbar) {
    class ToolBarDialog extends JDialog {

      public ToolBarDialog(Frame owner, String title, boolean modal) {
        super(owner, title, modal);
      }

      public ToolBarDialog(Dialog owner, String title, boolean modal) {
        super(owner, title, modal);
      }

      // Override createRootPane() to automatically resize
      // the frame when contents change
      protected JRootPane createRootPane() {
        JRootPane rootPane = new JRootPane() {
          private boolean packing = false;

          public void validate() {
            super.validate();
            if (!packing) {
              packing = true;
              pack();
              packing = false;
            }
          }
        };
        rootPane.setOpaque(true);
        return rootPane;
      }
    }

    JDialog dialog;
    Window window = SwingUtilities.getWindowAncestor(toolbar);
    if (window instanceof Frame) {
      dialog = new ToolBarDialog((Frame) window, toolbar.getName(), false);
    } else if (window instanceof Dialog) {
      dialog = new ToolBarDialog((Dialog) window, toolbar.getName(), false);
    } else {
      dialog = new ToolBarDialog((Frame) null, toolbar.getName(), false);
    }

    dialog.getRootPane().setName("ToolBar.FloatingWindow");
    dialog.setTitle(toolbar.getName());
    dialog.setResizable(false);
    WindowListener wl = createFrameListener();
    dialog.addWindowListener(wl);
    return dialog;
  }

  protected DragWindow createDragWindow(JToolBar toolbar) {
    Window frame = null;
    if (toolBar != null) {
      Container p;
      for (p = toolBar.getParent(); p != null && !(p instanceof Window);
          p = p.getParent()) {
        ;
      }
      if (p != null && p instanceof Window) {
        frame = (Window) p;
      }
    }
    if (floatingToolBar == null) {
      floatingToolBar = createFloatingWindow(toolBar);
    }
    if (floatingToolBar instanceof Window) {
      frame = (Window) floatingToolBar;
    }
    DragWindow dragWindow = new DragWindow(frame);
    return dragWindow;
  }

  /**
   * Returns a flag to determine whether rollover button borders
   * are enabled.
   *
   * @return true if rollover borders are enabled; false otherwise
   * @see #setRolloverBorders
   * @since 1.4
   */
  public boolean isRolloverBorders() {
    return rolloverBorders;
  }

  /**
   * Sets the flag for enabling rollover borders on the toolbar and it will
   * also install the appropriate border depending on the state of the flag.
   *
   * @param rollover if true, rollover borders are installed. Otherwise non-rollover borders are
   * installed
   * @see #isRolloverBorders
   * @since 1.4
   */
  public void setRolloverBorders(boolean rollover) {
    rolloverBorders = rollover;

    if (rolloverBorders) {
      installRolloverBorders(toolBar);
    } else {
      installNonRolloverBorders(toolBar);
    }
  }

  /**
   * Installs rollover borders on all the child components of the JComponent.
   * <p>
   * This is a convenience method to call <code>setBorderToRollover</code>
   * for each child component.
   *
   * @param c container which holds the child components (usually a JToolBar)
   * @see #setBorderToRollover
   * @since 1.4
   */
  protected void installRolloverBorders(JComponent c) {
    // Put rollover borders on buttons
    Component[] components = c.getComponents();

    for (Component component : components) {
      if (component instanceof JComponent) {
        ((JComponent) component).updateUI();
        setBorderToRollover(component);
      }
    }
  }

  /**
   * Installs non-rollover borders on all the child components of the JComponent.
   * A non-rollover border is the border that is installed on the child component
   * while it is in the toolbar.
   * <p>
   * This is a convenience method to call <code>setBorderToNonRollover</code>
   * for each child component.
   *
   * @param c container which holds the child components (usually a JToolBar)
   * @see #setBorderToNonRollover
   * @since 1.4
   */
  protected void installNonRolloverBorders(JComponent c) {
    // Put non-rollover borders on buttons. These borders reduce the margin.
    Component[] components = c.getComponents();

    for (Component component : components) {
      if (component instanceof JComponent) {
        ((JComponent) component).updateUI();
        setBorderToNonRollover(component);
      }
    }
  }

  /**
   * Installs normal borders on all the child components of the JComponent.
   * A normal border is the original border that was installed on the child
   * component before it was added to the toolbar.
   * <p>
   * This is a convenience method to call <code>setBorderNormal</code>
   * for each child component.
   *
   * @param c container which holds the child components (usually a JToolBar)
   * @see #setBorderToNonRollover
   * @since 1.4
   */
  protected void installNormalBorders(JComponent c) {
    // Put back the normal borders on buttons
    Component[] components = c.getComponents();

    for (Component component : components) {
      setBorderToNormal(component);
    }
  }

  /**
   * Sets the border of the component to have a rollover border which
   * was created by the {@link #createRolloverBorder} method.
   *
   * @param c component which will have a rollover border installed
   * @see #createRolloverBorder
   * @since 1.4
   */
  protected void setBorderToRollover(Component c) {
    if (c instanceof AbstractButton) {
      AbstractButton b = (AbstractButton) c;

      Border border = borderTable.get(b);
      if (border == null || border instanceof UIResource) {
        borderTable.put(b, b.getBorder());
      }

      // Only set the border if its the default border
      if (b.getBorder() instanceof UIResource) {
        b.setBorder(getRolloverBorder(b));
      }

      rolloverTable.put(b, b.isRolloverEnabled() ?
          Boolean.TRUE : Boolean.FALSE);
      b.setRolloverEnabled(true);
    }
  }

  /**
   * Returns a rollover border for the button.
   *
   * @param b the button to calculate the rollover border for
   * @return the rollover border
   * @see #setBorderToRollover
   * @since 1.6
   */
  protected Border getRolloverBorder(AbstractButton b) {
    return rolloverBorder;
  }

  /**
   * Sets the border of the component to have a non-rollover border which
   * was created by the {@link #createNonRolloverBorder} method.
   *
   * @param c component which will have a non-rollover border installed
   * @see #createNonRolloverBorder
   * @since 1.4
   */
  protected void setBorderToNonRollover(Component c) {
    if (c instanceof AbstractButton) {
      AbstractButton b = (AbstractButton) c;

      Border border = borderTable.get(b);
      if (border == null || border instanceof UIResource) {
        borderTable.put(b, b.getBorder());
      }

      // Only set the border if its the default border
      if (b.getBorder() instanceof UIResource) {
        b.setBorder(getNonRolloverBorder(b));
      }
      rolloverTable.put(b, b.isRolloverEnabled() ?
          Boolean.TRUE : Boolean.FALSE);
      b.setRolloverEnabled(false);
    }
  }

  /**
   * Returns a non-rollover border for the button.
   *
   * @param b the button to calculate the non-rollover border for
   * @return the non-rollover border
   * @see #setBorderToNonRollover
   * @since 1.6
   */
  protected Border getNonRolloverBorder(AbstractButton b) {
    if (b instanceof JToggleButton) {
      return nonRolloverToggleBorder;
    } else {
      return nonRolloverBorder;
    }
  }

  /**
   * Sets the border of the component to have a normal border.
   * A normal border is the original border that was installed on the child
   * component before it was added to the toolbar.
   *
   * @param c component which will have a normal border re-installed
   * @see #createNonRolloverBorder
   * @since 1.4
   */
  protected void setBorderToNormal(Component c) {
    if (c instanceof AbstractButton) {
      AbstractButton b = (AbstractButton) c;

      Border border = borderTable.remove(b);
      b.setBorder(border);

      Boolean value = rolloverTable.remove(b);
      if (value != null) {
        b.setRolloverEnabled(value.booleanValue());
      }
    }
  }

  public void setFloatingLocation(int x, int y) {
    floatingX = x;
    floatingY = y;
  }

  public boolean isFloating() {
    return floating;
  }

  public void setFloating(boolean b, Point p) {
    if (toolBar.isFloatable()) {
      boolean visible = false;
      Window ancestor = SwingUtilities.getWindowAncestor(toolBar);
      if (ancestor != null) {
        visible = ancestor.isVisible();
      }
      if (dragWindow != null) {
        dragWindow.setVisible(false);
      }
      this.floating = b;
      if (floatingToolBar == null) {
        floatingToolBar = createFloatingWindow(toolBar);
      }
      if (b == true) {
        if (dockingSource == null) {
          dockingSource = toolBar.getParent();
          dockingSource.remove(toolBar);
        }
        constraintBeforeFloating = calculateConstraint();
        if (propertyListener != null) {
          UIManager.addPropertyChangeListener(propertyListener);
        }
        floatingToolBar.getContentPane().add(toolBar, BorderLayout.CENTER);
        if (floatingToolBar instanceof Window) {
          ((Window) floatingToolBar).pack();
          ((Window) floatingToolBar).setLocation(floatingX, floatingY);
          if (visible) {
            ((Window) floatingToolBar).show();
          } else {
            ancestor.addWindowListener(new WindowAdapter() {
              public void windowOpened(WindowEvent e) {
                ((Window) floatingToolBar).show();
              }
            });
          }
        }
      } else {
        if (floatingToolBar == null) {
          floatingToolBar = createFloatingWindow(toolBar);
        }
        if (floatingToolBar instanceof Window) {
          ((Window) floatingToolBar).setVisible(false);
        }
        floatingToolBar.getContentPane().remove(toolBar);
        String constraint = getDockingConstraint(dockingSource,
            p);
        if (constraint == null) {
          constraint = BorderLayout.NORTH;
        }
        int orientation = mapConstraintToOrientation(constraint);
        setOrientation(orientation);
        if (dockingSource == null) {
          dockingSource = toolBar.getParent();
        }
        if (propertyListener != null) {
          UIManager.removePropertyChangeListener(propertyListener);
        }
        dockingSource.add(constraint, toolBar);
      }
      dockingSource.invalidate();
      Container dockingSourceParent = dockingSource.getParent();
      if (dockingSourceParent != null) {
        dockingSourceParent.validate();
      }
      dockingSource.repaint();
    }
  }

  private int mapConstraintToOrientation(String constraint) {
    int orientation = toolBar.getOrientation();

    if (constraint != null) {
      if (constraint.equals(BorderLayout.EAST) || constraint.equals(BorderLayout.WEST)) {
        orientation = JToolBar.VERTICAL;
      } else if (constraint.equals(BorderLayout.NORTH) || constraint.equals(BorderLayout.SOUTH)) {
        orientation = JToolBar.HORIZONTAL;
      }
    }

    return orientation;
  }

  public void setOrientation(int orientation) {
    toolBar.setOrientation(orientation);

    if (dragWindow != null) {
      dragWindow.setOrientation(orientation);
    }
  }

  /**
   * Gets the color displayed when over a docking area
   */
  public Color getDockingColor() {
    return dockingColor;
  }

  /**
   * Sets the color displayed when over a docking area
   */
  public void setDockingColor(Color c) {
    this.dockingColor = c;
  }

  /**
   * Gets the color displayed when over a floating area
   */
  public Color getFloatingColor() {
    return floatingColor;
  }

  /**
   * Sets the color displayed when over a floating area
   */
  public void setFloatingColor(Color c) {
    this.floatingColor = c;
  }

  private boolean isBlocked(Component comp, Object constraint) {
    if (comp instanceof Container) {
      Container cont = (Container) comp;
      LayoutManager lm = cont.getLayout();
      if (lm instanceof BorderLayout) {
        BorderLayout blm = (BorderLayout) lm;
        Component c = blm.getLayoutComponent(cont, constraint);
        return (c != null && c != toolBar);
      }
    }
    return false;
  }

  public boolean canDock(Component c, Point p) {
    return (p != null && getDockingConstraint(c, p) != null);
  }

  private String calculateConstraint() {
    String constraint = null;
    LayoutManager lm = dockingSource.getLayout();
    if (lm instanceof BorderLayout) {
      constraint = (String) ((BorderLayout) lm).getConstraints(toolBar);
    }
    return (constraint != null) ? constraint : constraintBeforeFloating;
  }


  private String getDockingConstraint(Component c, Point p) {
    if (p == null) {
      return constraintBeforeFloating;
    }
    if (c.contains(p)) {
      dockingSensitivity = (toolBar.getOrientation() == JToolBar.HORIZONTAL)
          ? toolBar.getSize().height
          : toolBar.getSize().width;
      // North  (Base distance on height for now!)
      if (p.y < dockingSensitivity && !isBlocked(c, BorderLayout.NORTH)) {
        return BorderLayout.NORTH;
      }
      // East  (Base distance on height for now!)
      if (p.x >= c.getWidth() - dockingSensitivity && !isBlocked(c, BorderLayout.EAST)) {
        return BorderLayout.EAST;
      }
      // West  (Base distance on height for now!)
      if (p.x < dockingSensitivity && !isBlocked(c, BorderLayout.WEST)) {
        return BorderLayout.WEST;
      }
      if (p.y >= c.getHeight() - dockingSensitivity && !isBlocked(c, BorderLayout.SOUTH)) {
        return BorderLayout.SOUTH;
      }
    }
    return null;
  }

  protected void dragTo(Point position, Point origin) {
    if (toolBar.isFloatable()) {
      try {
        if (dragWindow == null) {
          dragWindow = createDragWindow(toolBar);
        }
        Point offset = dragWindow.getOffset();
        if (offset == null) {
          Dimension size = toolBar.getPreferredSize();
          offset = new Point(size.width / 2, size.height / 2);
          dragWindow.setOffset(offset);
        }
        Point global = new Point(origin.x + position.x,
            origin.y + position.y);
        Point dragPoint = new Point(global.x - offset.x,
            global.y - offset.y);
        if (dockingSource == null) {
          dockingSource = toolBar.getParent();
        }
        constraintBeforeFloating = calculateConstraint();
        Point dockingPosition = dockingSource.getLocationOnScreen();
        Point comparisonPoint = new Point(global.x - dockingPosition.x,
            global.y - dockingPosition.y);
        if (canDock(dockingSource, comparisonPoint)) {
          dragWindow.setBackground(getDockingColor());
          String constraint = getDockingConstraint(dockingSource,
              comparisonPoint);
          int orientation = mapConstraintToOrientation(constraint);
          dragWindow.setOrientation(orientation);
          dragWindow.setBorderColor(dockingBorderColor);
        } else {
          dragWindow.setBackground(getFloatingColor());
          dragWindow.setBorderColor(floatingBorderColor);
          dragWindow.setOrientation(toolBar.getOrientation());
        }

        dragWindow.setLocation(dragPoint.x, dragPoint.y);
        if (dragWindow.isVisible() == false) {
          Dimension size = toolBar.getPreferredSize();
          dragWindow.setSize(size.width, size.height);
          dragWindow.show();
        }
      } catch (IllegalComponentStateException e) {
      }
    }
  }

  protected void floatAt(Point position, Point origin) {
    if (toolBar.isFloatable()) {
      try {
        Point offset = dragWindow.getOffset();
        if (offset == null) {
          offset = position;
          dragWindow.setOffset(offset);
        }
        Point global = new Point(origin.x + position.x,
            origin.y + position.y);
        setFloatingLocation(global.x - offset.x,
            global.y - offset.y);
        if (dockingSource != null) {
          Point dockingPosition = dockingSource.getLocationOnScreen();
          Point comparisonPoint = new Point(global.x - dockingPosition.x,
              global.y - dockingPosition.y);
          if (canDock(dockingSource, comparisonPoint)) {
            setFloating(false, comparisonPoint);
          } else {
            setFloating(true, null);
          }
        } else {
          setFloating(true, null);
        }
        dragWindow.setOffset(null);
      } catch (IllegalComponentStateException e) {
      }
    }
  }

  private Handler getHandler() {
    if (handler == null) {
      handler = new Handler();
    }
    return handler;
  }

  protected ContainerListener createToolBarContListener() {
    return getHandler();
  }

  protected FocusListener createToolBarFocusListener() {
    return getHandler();
  }

  protected PropertyChangeListener createPropertyListener() {
    return getHandler();
  }

  protected MouseInputListener createDockingListener() {
    getHandler().tb = toolBar;
    return getHandler();
  }

  protected WindowListener createFrameListener() {
    return new FrameListener();
  }

  /**
   * Paints the contents of the window used for dragging.
   *
   * @param g Graphics to paint to.
   * @throws NullPointerException is <code>g</code> is null
   * @since 1.5
   */
  protected void paintDragWindow(Graphics g) {
    g.setColor(dragWindow.getBackground());
    int w = dragWindow.getWidth();
    int h = dragWindow.getHeight();
    g.fillRect(0, 0, w, h);
    g.setColor(dragWindow.getBorderColor());
    g.drawRect(0, 0, w - 1, h - 1);
  }


  private static class Actions extends UIAction {

    private static final String NAVIGATE_RIGHT = "navigateRight";
    private static final String NAVIGATE_LEFT = "navigateLeft";
    private static final String NAVIGATE_UP = "navigateUp";
    private static final String NAVIGATE_DOWN = "navigateDown";

    public Actions(String name) {
      super(name);
    }

    public void actionPerformed(ActionEvent evt) {
      String key = getName();
      JToolBar toolBar = (JToolBar) evt.getSource();
      BasicToolBarUI ui = (BasicToolBarUI) BasicLookAndFeel.getUIOfType(
          toolBar.getUI(), BasicToolBarUI.class);

      if (NAVIGATE_RIGHT == key) {
        ui.navigateFocusedComp(EAST);
      } else if (NAVIGATE_LEFT == key) {
        ui.navigateFocusedComp(WEST);
      } else if (NAVIGATE_UP == key) {
        ui.navigateFocusedComp(NORTH);
      } else if (NAVIGATE_DOWN == key) {
        ui.navigateFocusedComp(SOUTH);
      }
    }
  }


  private class Handler implements ContainerListener,
      FocusListener, MouseInputListener, PropertyChangeListener {

    //
    // ContainerListener
    //
    public void componentAdded(ContainerEvent evt) {
      Component c = evt.getChild();

      if (toolBarFocusListener != null) {
        c.addFocusListener(toolBarFocusListener);
      }

      if (isRolloverBorders()) {
        setBorderToRollover(c);
      } else {
        setBorderToNonRollover(c);
      }
    }

    public void componentRemoved(ContainerEvent evt) {
      Component c = evt.getChild();

      if (toolBarFocusListener != null) {
        c.removeFocusListener(toolBarFocusListener);
      }

      // Revert the button border
      setBorderToNormal(c);
    }


    //
    // FocusListener
    //
    public void focusGained(FocusEvent evt) {
      Component c = evt.getComponent();
      focusedCompIndex = toolBar.getComponentIndex(c);
    }

    public void focusLost(FocusEvent evt) {
    }


    //
    // MouseInputListener (DockingListener)
    //
    JToolBar tb;
    boolean isDragging = false;
    Point origin = null;

    public void mousePressed(MouseEvent evt) {
      if (!tb.isEnabled()) {
        return;
      }
      isDragging = false;
    }

    public void mouseReleased(MouseEvent evt) {
      if (!tb.isEnabled()) {
        return;
      }
      if (isDragging) {
        Point position = evt.getPoint();
        if (origin == null) {
          origin = evt.getComponent().getLocationOnScreen();
        }
        floatAt(position, origin);
      }
      origin = null;
      isDragging = false;
    }

    public void mouseDragged(MouseEvent evt) {
      if (!tb.isEnabled()) {
        return;
      }
      isDragging = true;
      Point position = evt.getPoint();
      if (origin == null) {
        origin = evt.getComponent().getLocationOnScreen();
      }
      dragTo(position, origin);
    }

    public void mouseClicked(MouseEvent evt) {
    }

    public void mouseEntered(MouseEvent evt) {
    }

    public void mouseExited(MouseEvent evt) {
    }

    public void mouseMoved(MouseEvent evt) {
    }


    //
    // PropertyChangeListener
    //
    public void propertyChange(PropertyChangeEvent evt) {
      String propertyName = evt.getPropertyName();
      if (propertyName == "lookAndFeel") {
        toolBar.updateUI();
      } else if (propertyName == "orientation") {
        // Search for JSeparator components and change it's orientation
        // to match the toolbar and flip it's orientation.
        Component[] components = toolBar.getComponents();
        int orientation = ((Integer) evt.getNewValue()).intValue();
        JToolBar.Separator separator;

        for (int i = 0; i < components.length; ++i) {
          if (components[i] instanceof JToolBar.Separator) {
            separator = (JToolBar.Separator) components[i];
            if ((orientation == JToolBar.HORIZONTAL)) {
              separator.setOrientation(JSeparator.VERTICAL);
            } else {
              separator.setOrientation(JSeparator.HORIZONTAL);
            }
            Dimension size = separator.getSeparatorSize();
            if (size != null && size.width != size.height) {
              // Flip the orientation.
              Dimension newSize =
                  new Dimension(size.height, size.width);
              separator.setSeparatorSize(newSize);
            }
          }
        }
      } else if (propertyName == IS_ROLLOVER) {
        installNormalBorders(toolBar);
        setRolloverBorders(((Boolean) evt.getNewValue()).booleanValue());
      }
    }
  }

  protected class FrameListener extends WindowAdapter {

    public void windowClosing(WindowEvent w) {
      if (toolBar.isFloatable()) {
        if (dragWindow != null) {
          dragWindow.setVisible(false);
        }
        floating = false;
        if (floatingToolBar == null) {
          floatingToolBar = createFloatingWindow(toolBar);
        }
        if (floatingToolBar instanceof Window) {
          ((Window) floatingToolBar).setVisible(false);
        }
        floatingToolBar.getContentPane().remove(toolBar);
        String constraint = constraintBeforeFloating;
        if (toolBar.getOrientation() == JToolBar.HORIZONTAL) {
          if (constraint == "West" || constraint == "East") {
            constraint = "North";
          }
        } else {
          if (constraint == "North" || constraint == "South") {
            constraint = "West";
          }
        }
        if (dockingSource == null) {
          dockingSource = toolBar.getParent();
        }
        if (propertyListener != null) {
          UIManager.removePropertyChangeListener(propertyListener);
        }
        dockingSource.add(toolBar, constraint);
        dockingSource.invalidate();
        Container dockingSourceParent = dockingSource.getParent();
        if (dockingSourceParent != null) {
          dockingSourceParent.validate();
        }
        dockingSource.repaint();
      }
    }

  }

  protected class ToolBarContListener implements ContainerListener {

    // NOTE: This class exists only for backward compatibility. All
    // its functionality has been moved into Handler. If you need to add
    // new functionality add it to the Handler, but make sure this
    // class calls into the Handler.
    public void componentAdded(ContainerEvent e) {
      getHandler().componentAdded(e);
    }

    public void componentRemoved(ContainerEvent e) {
      getHandler().componentRemoved(e);
    }

  }

  protected class ToolBarFocusListener implements FocusListener {

    // NOTE: This class exists only for backward compatibility. All
    // its functionality has been moved into Handler. If you need to add
    // new functionality add it to the Handler, but make sure this
    // class calls into the Handler.
    public void focusGained(FocusEvent e) {
      getHandler().focusGained(e);
    }

    public void focusLost(FocusEvent e) {
      getHandler().focusLost(e);
    }
  }

  protected class PropertyListener implements PropertyChangeListener {

    // NOTE: This class exists only for backward compatibility. All
    // its functionality has been moved into Handler. If you need to add
    // new functionality add it to the Handler, but make sure this
    // class calls into the Handler.
    public void propertyChange(PropertyChangeEvent e) {
      getHandler().propertyChange(e);
    }
  }

  /**
   * This class should be treated as a &quot;protected&quot; inner class.
   * Instantiate it only within subclasses of BasicToolBarUI.
   */
  public class DockingListener implements MouseInputListener {

    // NOTE: This class exists only for backward compatibility. All
    // its functionality has been moved into Handler. If you need to add
    // new functionality add it to the Handler, but make sure this
    // class calls into the Handler.
    protected JToolBar toolBar;
    protected boolean isDragging = false;
    protected Point origin = null;

    public DockingListener(JToolBar t) {
      this.toolBar = t;
      getHandler().tb = t;
    }

    public void mouseClicked(MouseEvent e) {
      getHandler().mouseClicked(e);
    }

    public void mousePressed(MouseEvent e) {
      getHandler().tb = toolBar;
      getHandler().mousePressed(e);
      isDragging = getHandler().isDragging;
    }

    public void mouseReleased(MouseEvent e) {
      getHandler().tb = toolBar;
      getHandler().isDragging = isDragging;
      getHandler().origin = origin;
      getHandler().mouseReleased(e);
      isDragging = getHandler().isDragging;
      origin = getHandler().origin;
    }

    public void mouseEntered(MouseEvent e) {
      getHandler().mouseEntered(e);
    }

    public void mouseExited(MouseEvent e) {
      getHandler().mouseExited(e);
    }

    public void mouseDragged(MouseEvent e) {
      getHandler().tb = toolBar;
      getHandler().origin = origin;
      getHandler().mouseDragged(e);
      isDragging = getHandler().isDragging;
      origin = getHandler().origin;
    }

    public void mouseMoved(MouseEvent e) {
      getHandler().mouseMoved(e);
    }
  }

  protected class DragWindow extends Window {

    Color borderColor = Color.gray;
    int orientation = toolBar.getOrientation();
    Point offset; // offset of the mouse cursor inside the DragWindow

    DragWindow(Window w) {
      super(w);
    }

    /**
     * Returns the orientation of the toolbar window when the toolbar is
     * floating. The orientation is either one of <code>JToolBar.HORIZONTAL</code>
     * or <code>JToolBar.VERTICAL</code>.
     *
     * @return the orientation of the toolbar window
     * @since 1.6
     */
    public int getOrientation() {
      return orientation;
    }

    public void setOrientation(int o) {
      if (isShowing()) {
        if (o == this.orientation) {
          return;
        }
        this.orientation = o;
        Dimension size = getSize();
        setSize(new Dimension(size.height, size.width));
        if (offset != null) {
          if (BasicGraphicsUtils.isLeftToRight(toolBar)) {
            setOffset(new Point(offset.y, offset.x));
          } else if (o == JToolBar.HORIZONTAL) {
            setOffset(new Point(size.height - offset.y, offset.x));
          } else {
            setOffset(new Point(offset.y, size.width - offset.x));
          }
        }
        repaint();
      }
    }

    public Point getOffset() {
      return offset;
    }

    public void setOffset(Point p) {
      this.offset = p;
    }

    public void setBorderColor(Color c) {
      if (this.borderColor == c) {
        return;
      }
      this.borderColor = c;
      repaint();
    }

    public Color getBorderColor() {
      return this.borderColor;
    }

    public void paint(Graphics g) {
      paintDragWindow(g);
      // Paint the children
      super.paint(g);
    }

    public Insets getInsets() {
      return new Insets(1, 1, 1, 1);
    }
  }
}
